iT邦幫忙

1

Neo4j 應用練習: 金融詐欺檢測

  • 分享至 

  • xImage
  •  

前言

在上一篇文章中 Neo4j Graph Data Science 基本語法與演算法實作資料分析,我重新介紹了 Neo4j GDS 的基本概念與操作方式,以及一部分的演算法分析資料,這次我想挑戰更進階的應用:金融詐欺檢測,使用 Neo4j Sandbox 提供的分析步驟與方法來練習,其中 Fraud Detection 專案便使用了 PaySim 數據,以及一連串的 Neo4j Cypher 和 GDS 演算法,來模擬在龐大的金融交易紀錄中,找出金融詐欺的嫌犯。

因為是以練習 Cypher 與 GDS 演算法為目的,基本上都會與官方教學的範例與步驟相同,但一部分 Cypher 指令有做些修改或解釋,以及我沒有打算用官方做好的資料,而是用 PaySim 重新模擬出更大量的數據,模擬的方式也會一併分享。

為什麼要用 PaySim,它是什麼?

對於不是直接在銀行或保險業上班的人,根本無法取得真實世界中的交易資料,即便是銀行職員也不見得有這樣的權限。所幸 Lopez-Rojas、Elmire 和 Axelsson 在 2016 年發布了 PaySim 模擬工具。
它是一個行動支付的交易紀錄模擬器,可以想像成 Apple Pay,有電子錢包的功能可以儲值,用戶間可以互相轉帳匯款,或是與商家進行交易,也可以把錢轉出轉入銀行,這些資料參考了真實的商務系統產生的交易日誌,進一步模擬與合成。

Neo4j Sandbox 提供的 PaySim 資料集大約有 33 萬個資料節點、100 萬筆關聯,其中包含 2500 位客戶、32 萬筆交易紀錄。
如果想自己產生不同的數據,可以參考 PaySim Github,但輸出的格式是CSV,且教學有點少,還要自行匯入 Neo4j

但是後來一位 Neo4j 工程師 Dave Voutila,他 FORK 出一個新的 Github project https://github.com/voutilad/PaySim ,把原本的工具改寫成 Library,以便可以被其他 Java 程式直接調用,並改善原本詐欺行為模式過於單純的問題,為詐欺者加入更多的行為模式,以便貼近真實世界的案例。
之後他再開發了一個 PaySim demo project ,使用上述改好的 PaySim Library,搭配一篇很完整的教學文章 https://www.sisu.io/posts/paysim-part2/ ,教大家如何用 PaySim 直接匯入 Neo4j 的資料庫,省略了中間 CSV 轉換的過程,真是太佛心了!

如何產生自己想要的 PaySim 交易數據

這一段會分享如何用 Dave Voutila 的 PaySim demo project 來自定義產生的 PaySim 數據,並直接匯入 Neo4j,如果沒有這個需求,可以直接略過。
paysim-demo 是一個 gradle wrapper 專案,git clone 之後直接用 gradle wrapper 執行即可

./gradlew runBolt

預設會用 bolt protocol 連線到本機 Neo4j 7687 port,設定檔請參考 src/main/java/io/sisu/paysim/Config.java,也可以在這裡設定你自己的資料庫帳密。

不過仍建議直接打包成可執行檔,以上的參數都可以用參數傳遞

./gradlew distTar

打包完成後,會在 build/ 得到 tar 檔案,例如 ./build/distributions/paysim-demo-0.8.3.tar
以下是我執行的指令

bin/paysim-demo bolt --uri bolt://172.29.64.1:7687 --username neo4j --password 12345678

這邊 bolt connection 不是預設的 localhost,而是我的電腦 private ip,因為我是在 WSL2 Ubuntu 執行 paysim-demo,匯入到 Windows 的 Neo4j。除了要明確指定匯出的 IP,也要設定 Neo4j 允許 remote connection

dbms.default_listen_address=0.0.0.0

客製化數據的部分,則要修改 PaySim.properties,以下是我的參數,我直接用註解來說明。

seed=666666  # 修改 seed 來產出不同特徵的資料
nbSteps=720  # 最大值 720 步,一步相當於於 1 小時,也就是最多模擬真實世界一個月的交易量,這是源自 PaySim 原本的限制
multiplier=1 # 方便分析師快速的加倍資料量,請小心使用。2 的意思就是以下參數全部乘以 2
nbClients=80000   # 產生大約 8 萬名客戶
nbFraudsters=2000
nbMerchants=3000
nbBanks=5

以上的參數為我重新產出了約 1410 萬個資料節點、4160 萬個關聯,80000 個客戶、3000 家合作商店、5 家銀行、1370 萬筆交易紀錄。
https://ithelp.ithome.com.tw/upload/images/20220911/200929125zQeL46DOL.png

不過因為這個數據比官方的版本大得多,所以我也一併重新調整了記憶體相關的參數如下,僅供參考。這部分的優化可參考官方文件 guide-performance-tuningmemory-configuration

dbms.memory.heap.initial_size=24G
dbms.memory.heap.max_size=24G
dbms.memory.pagecache.size=28G
dbms.memory.transaction.global_max_size=8G
dbms.memory.transaction.max_size=4G

想解決什麼問題?

Fraud Detection 專案一開始就指出了兩種詐欺行為,作為分析資料集的目標

  • First party fraudsters (第一方詐欺犯)
  • Money Mules (金錢驢子,簡稱錢驢)
    但是在繼續深入之前,我想要先搞懂這兩種詐欺行為是怎麼回事,總要先有一些 Domain Knowledge,才不會為了分析而分析。

詐欺簡單分類

在網路上翻了一輪,找到 experian(一家美國-愛爾蘭跨國消費者信用報告公司) 的文章 The different types of fraud and how they’re changing,詳細介紹了詐欺的分類與常見行為模式如下。

  • 第一方詐欺 (First-party Fraud)
    個人或群體藉由謊報其身分,或故意給予錯誤資訊來取得商業上的利益。例如誇大其收入、假造公司經營與員工就業狀況,來獲得額外的服務和信貸。常見行為如下:
  1. 以他人的名義設立服務以節省資金
  2. 以不同風險或有不同費率的地址來申請服務
  3. 網路購物後拒絕承認其購物行為,以此申請退款
  4. 購買可以全額退款的商品,例如衣服,然後謊稱瑕疵品來取得退款
  5. 線上購物後謊稱未收到貨,或物品瑕疵,或退回空箱以取得退款

總歸來說,個人或群體歪曲其身份或提供假訊息來獲得利益。

  • 第二方詐欺 (Second-party Fraud)

意指個人故意將其身分或資料提供給他人進行詐欺,也就是上一小節提到的「錢驢」。常見的就是提供人頭帳戶給詐騙集團在銀行出入資金,以換取少量報酬,而這種詐欺金額還很有可能是犯罪活動或洗錢的結果,可用於資助恐怖主義、剝削和暴力犯罪。
年輕人和弱勢群體一直是第二方欺詐的目標。社交媒體已被大量用於以這種方式針對年輕人和弱勢群體。

  • 第三方詐欺 (Third-party Fraud)

通常就的是身份盜用,也就是未經個人同意,在其不知情下使用其身份獲得信用、服務或產品,這還包括製造假的身份,例如藉由收集來的非法個資去憑空創造新的身份。

如果自己 Google「三方詐騙」,就會發現網路上許多報導,各地警局也有貼出告示作為警惕,因為這種詐騙充斥你我的生活之中,防不勝防。

從上述三種詐欺形式來看,可以發現一個共通點:詐欺行為的發生,或多或少會伴隨著個人身份的盜用與共享。

回到 PaySim,先從了解資料開始

了解資料集的結構與分布概況,已經是資料分析的起手式了
可直接參考 Dave Voutila 的文章介紹 PaySim資料模型
或是自行用 Cypher 語法得知

CALL db.schema.visualization();
CALL apoc.meta.subGraph({
    labels: ['Client', 'Email', 'Phone', 'SSN', 'Transaction', 'Bank', 'Merchant']
})

https://ithelp.ithome.com.tw/upload/images/20220905/200929128hyn8D6g8O.png

從這個資料結構可以看出,每一位客戶(Client),有其個人資料,目前只有 Email、電話和美國社會安全碼SSN,這是簡化過的模型,實際上的個人資料當然會更多。客戶同一時間會和其他客戶、銀行、商家有零到多筆交易,這個資料模型聚焦在客戶發起的交易行為,也是為了學習資料分析而簡化過的,實際上交易行為應該是因商業模式變化,多向性且複雜,例如商家與商家,或商家與銀行。

※ APOC Library 是 Neo4j 的實驗室專案之一,集合了大約 400 個常用的預存程序或函數,可參考我之前的文章介紹
https://ithelp.ithome.com.tw/articles/10244205

查詢資料分布

使用 APOC 提供的函數,直接可得知這個資料集有些什麼樣的節點和關聯,以及數量

CALL apoc.meta.stats();

官方教學仍列出了自行用 Cypher 語法查詢的方式,如下。

CALL db.labels() YIELD label
CALL apoc.cypher.run('MATCH (:`'+label+'`) RETURN count(*) as count', {})
YIELD value
RETURN label, value.count AS Count

CALL db.relationshipTypes() YIELD relationshipType as type
CALL apoc.cypher.run('MATCH ()-[:`'+type+'`]->() RETURN count(*) as count', {})
YIELD value
RETURN type, value.count AS Count

查詢所有的交易類型與交易總額的百分比例

MATCH (t:Transaction)
WITH count(t) AS globalCnt, sum(t.amount) as totalAmount
UNWIND ['CashIn', 'CashOut', 'Payment', 'Debit', 'Transfer'] AS txType
  CALL apoc.cypher.run('MATCH (t:' + txType + ') RETURN count(t) AS txCnt, sum(t.amount) AS txAmount', {}) YIELD value
RETURN
  txType,
  value.txCnt AS NumberOfTransactions,
  round(toFloat(value.txCnt) * 100 / toFloat(globalCnt), 2) AS `%Transactions`,
  round(toFloat(value.txAmount) * 100 / toFloat(totalAmount), 2) AS `%Amount`
ORDER BY NumberOfTransactions DESC;

https://ithelp.ithome.com.tw/upload/images/20220911/20092912uy2IDh2zS8.png
從上圖可以看出,最大的交易量和最大交易額都是 CashIn,比較特別的是用戶間的轉帳 Transfer,交易量只占 6%,但交易總額卻高達 36% 之多!

這邊我發現一個有趣的點,雖然有點事後諸葛~
就是所有的交易類節點,都必然有 Transaction 標籤,這感覺很像一種「繼承」的概念,我自己可能受到傳統關聯式資料庫的荼毒太深了XD,所以比較少設計多重標籤,但上述的結構讓我重新思考 Neo4j 多重標籤的設計方式與可能性!

分析資料前的假設

官方教學提出了一個基本假設,就是「兩個用戶共享了個人身分資訊,例如電話、Email、SSN等,是可疑的,會有比較高的機率進行詐欺」,其實這樣的假設也是呼應了前面介紹詐欺分類的描述。
當然這不是絕對的,每一個共享其本質是沒問題的,但是被愈多人共享的個資是可疑的,而使用這些個資的用戶,就愈有可能是詐欺犯。這個基本假設可能來自於銀行的經驗吧。
所以我們需要有一些演算法來計算這些客戶的詐欺分數,將分數最高的客戶們取一個比例,才標示為詐欺犯。

找出所有至少共享一項個資的用戶,以及實際上共享的個資數量

MATCH (c1:Client)-[:HAS_EMAIL|:HAS_PHONE|:HAS_SSN]-> (n) <-[:HAS_EMAIL|:HAS_PHONE|:HAS_SSN]-(c2:Client)
WHERE id(c1) < id(c2)  // 為避免重複列出,所以用小於
RETURN c1.id, c2.id, count(*) AS freq
ORDER BY freq DESC;

找出總共有多少用戶共享身分資訊

MATCH (c1:Client)-[:HAS_EMAIL|:HAS_PHONE|:HAS_SSN]-> (n) <-[:HAS_EMAIL|:HAS_PHONE|:HAS_SSN]-(c2:Client)
WHERE id(c1) <> id(c2)
RETURN count(DISTINCT c1.id) AS freq;

針對有共享個資的用戶,將共享的雙方建立新的關聯 SHARED_IDENTIFIERS,並賦予屬性 count,表示共享的個資總數

MATCH (c1:Client)-[:HAS_EMAIL|:HAS_PHONE|:HAS_SSN] -> (n) <- [:HAS_EMAIL|:HAS_PHONE|:HAS_SSN]-(c2:Client)
WHERE id(c1) < id(c2)
WITH c1, c2, count(*) as cnt
MERGE (c1) - [:SHARED_IDENTIFIERS {count: cnt}] -> (c2);

建立關聯後,重新用圖形顯示出至少共享兩項個資的用戶群

MATCH p = (:Client) - [s:SHARED_IDENTIFIERS] -> (:Client) WHERE s.count >= 2 RETURN p;

https://ithelp.ithome.com.tw/upload/images/20220911/20092912wNph370YKw.png

到目前為止,都只是在深入分析資料前的簡單探索,對眼前的資料有多一點概念與理解,還不能直接做出任何判斷與識別。接下來就用 Neo4j GDS 演算法,從各個層面去逐步抽絲剝繭,找出最有可能是第一方詐欺的客戶。

分析共享個資的族群 - WCC 演算法

首先用 WCC 演算法 (Weakly Connected Components) 來做群聚分析,找到所有共享個資的用戶,並將其分群。
然後因為是千萬級別的資料量,所以在建立 Projection graph 或使用演算法之前,我會很常先估算記憶體用量如下,後續文章會省略不提。
https://ithelp.ithome.com.tw/upload/images/20220910/20092912pOnCtgIPG0.png

建立 WCC 算法要用的 Projection graph,將全部用戶以及 SHARED_IDENTIFIERS 關聯都放到記憶體圖中,並把關聯改為無向性。官方給的語法我稍嫌囉嗦,所以簡化如下

CALL gds.graph.project('wcc',
    'Client',
    {
        SHARED_IDENTIFIERS: {
            orientation: 'UNDIRECTED',
            properties: 'count'
        }
    }
) YIELD graphName,nodeCount,relationshipCount,projectMillis;

先跑一次 stream 模式查看分群結果,如圖,相同的 componentId 表示被分到同一群,而同一群代表用戶彼此之間或多或少有共用一些個資。

CALL gds.wcc.stream('wcc',
    {
        nodeLabels: ['Client'],
        relationshipTypes: ['SHARED_IDENTIFIERS'],
        consecutiveIds: true  // 讓 componentId 連號
    }
)
YIELD nodeId, componentId
RETURN gds.util.asNode(nodeId).id AS clientId, componentId
ORDER BY componentId;

https://ithelp.ithome.com.tw/upload/images/20220906/20092912VnzK2hSjL4.png

在官方教學文件中,WCC 的演算過程有加上 consecutiveIds: true 設定,這是讓最後輸出的 componentId 可以連號,因為 Component 在不斷的合併過程中,就會產生跳號。不過這個設定在這次的練習沒有實際用途,而且會耗用更多記憶體,不建議使用。

WCC 演算法還額外提供了 weight 和 threshold 的設定,以下便是限定 SHARED_IDENTIFIERS 的 count 屬性必須大於 1,才會被 WCC 算法視為兩個節點可有效連通。
意義上就是說,兩個用戶彼此間至少有兩項個資共用,其 SHARED_IDENTIFIERS 關聯才視為有效。

CALL gds.wcc.stream('wcc',
    {
        nodeLabels: ['Client'],
        relationshipTypes: ['SHARED_IDENTIFIERS'],
        consecutiveIds: true,
        relationshipWeightProperty: 'count',
        threshold: 1.0
    }
)

最後,我們將 WCC 演算法的分群結果,也就是 componentId,判斷 Component 有兩個客戶或以上時,將 componentId 寫回 firstPartyFraudGroupId 屬性,至此完成第一方詐欺分群。同一群內的所有人,彼此間一定有直接或間接共用 Email、Phone、SSN。
Component 內如果只有一個客戶,表示他沒有和任何人共用個資。

CALL gds.wcc.stream('wcc',
    {
        nodeLabels: ['Client'],
        relationshipTypes: ['SHARED_IDENTIFIERS']
    }
)
YIELD componentId as groupId, nodeId
WITH groupId, gds.util.asNode(nodeId) AS client
WITH groupId, collect(client) AS clients
WITH groupId, clients, size(clients) AS groupSize WHERE groupSize > 1
UNWIND clients AS client
    MATCH (c:Client) WHERE c.id = client.id
    SET c.firstPartyFraudGroupId = groupId;

這邊因為有刻意過濾出集群數量大於 1,也就是彼此有共享個資的群體,才寫入 firstPartyFraudGroupId 屬性,如果想要不管群體大小,一律寫入新的屬性,那麼簡單用 write 模式即可解決。

CALL gds.wcc.stream('wcc',
    {
        nodeLabels: ['Client'],
        relationshipTypes: ['SHARED_IDENTIFIERS'],
        writeProperty: 'firstPartyFraudGroupId' }
    }
)

最後將第一方詐欺分群內,比較密集的群組先找出來調查,以便縮小調查範圍,這邊以 6 個客戶以上的群體為例。(圖形中有很多群體不到六個客戶,純粹只是因為回傳數量大,被 Neo4j 限制了)
接著可以逐步提高數字來優先調查更大的群體。

MATCH (c:Client)
WITH c.firstPartyFraudGroupId AS groupId, collect(c.id) AS group
WITH *, size(group) AS groupSize WHERE groupSize >= 6
WITH collect(groupId) AS fraudRings
MATCH p = (c:Client)-[:HAS_SSN|HAS_EMAIL|HAS_PHONE]->() WHERE c.firstPartyFraudGroupId IN fraudRings
RETURN p

https://ithelp.ithome.com.tw/upload/images/20220911/20092912U5RH3fnhoE.png

從這個圖可以看到有些個資被許多客戶重複使用。以此為出發點,試著找出相似度高的客戶群,共用愈多個資,相似度就愈高。

找出高相似度的可疑客戶 - Node similarity 演算法

如之前文章介紹到 Node similarity 演算法,使用 Jaccard similarity 來計算相似度,而 Jaccard 會使用到二分圖,在目前的案例中,二分圖的第一群節點就是潛在的第一方詐欺客戶群,第二群節點就是個資群,我們要用個資群來計算客戶群彼此間的相似度分數。

先用 Projection graph 建立二分圖如下

MATCH (c:Client) WHERE c.firstPartyFraudGroupId is not NULL  // 第一群節點,初步懷疑為第一方詐欺
WITH collect(c) as clients
MATCH (n) WHERE n:Email OR n:Phone OR n:SSN  // 第二群節點為所有個資,用來輔助判斷第一群節點間的相似度
WITH clients, collect(n) as identifiers
WITH clients + identifiers as nodes

MATCH (c:Client) -[:HAS_EMAIL|:HAS_PHONE|:HAS_SSN]->(id)
WHERE c.firstPartyFraudGroupId is not NULL
// 收集所有第一方詐欺、所有個資、以及所有關聯,準備建立 Cypher projection graph
WITH nodes, collect({source: c, target: id}) as relationships

// 以下可參考官方 Cypher projection 定義 https://neo4j.com/docs/graph-data-science/current/graph-project-cypher/
CALL gds.graph.project.cypher('similarity',
    "UNWIND $nodes as n RETURN id(n) AS id, labels(n) AS labels",
    "UNWIND $relationships as r RETURN id(r['source']) AS source, id(r['target']) AS target, 'HAS_IDENTIFIER' as type",
    { parameters: {nodes: nodes, relationships: relationships}}
)
YIELD graphName, nodeCount, relationshipCount, projectMillis
RETURN graphName, nodeCount, relationshipCount, projectMillis

藉由上面準備好的 projection graph,使用 nodeSimilarity 演算法計算用戶間的相似度,給予高低評分並建立新的關聯

CALL gds.nodeSimilarity.mutate('similarity',
    {
        topK:15,  // 每個 Client 只找出相似度最高的 15 個 Client,數字愈高愈精確,但耗用更多記憶體,預設值是 10
        mutateProperty: 'jaccardScore',
        mutateRelationshipType:'SIMILAR_TO'
    }
);

將記憶體中的關聯與分數寫回到資料庫,作為後續分析

CALL gds.graph.writeRelationship('similarity', 'SIMILAR_TO', 'jaccardScore');

接著用圖形表示出,在第一方詐欺 6 個客戶以上的大群體中,彼此有高度相似的客戶們

MATCH (c:Client)
WITH c.firstPartyFraudGroupId AS groupId, collect(c.id) AS group
WITH *, size(group) AS groupSize WHERE groupSize >= 6
WITH collect(groupId) AS fraudRings
MATCH p = (c:Client)-[r:SIMILAR_TO]->() WHERE c.firstPartyFraudGroupId IN fraudRings AND r.jaccardScore > 0.8
RETURN p

https://ithelp.ithome.com.tw/upload/images/20220911/20092912i5gV8wm6Kp.png

找出影響力大的第一方詐欺 - Weighted Degree Centrality 演算法

繼續縮小範圍,接下來加總每個客戶的 jaccardScore 分數,得出 firstPartyFraudScore,這個分數愈高,代表這個客戶和愈多人有相似性,愈有可能是第一方詐欺

CALL gds.degree.stream('similarity',
    {
        nodeLabels: ['Client'],
        relationshipTypes: ['SIMILAR_TO'],
        relationshipWeightProperty: 'jaccardScore'
    }
)
YIELD nodeId, score
RETURN gds.util.asNode(nodeId).name AS name, score
ORDER BY score DESC

https://ithelp.ithome.com.tw/upload/images/20220906/20092912I3s2ZVlup1.png

將加總分數寫回資料庫 firstPartyFraudScore 屬性

CALL gds.degree.write('similarity',
    {
        nodeLabels: ['Client'],
        relationshipTypes: ['SIMILAR_TO'],
        relationshipWeightProperty: 'jaccardScore',
        writeProperty: 'firstPartyFraudScore'
    }
);

計算出 firstPartyFraudScore 的第 95 百分位數,作為審查的臨界值,超過這個值,就直接標籤他為詐欺犯

MATCH(c:Client)
WHERE c.firstPartyFraudScore IS NOT NULL
WITH percentileCont(c.firstPartyFraudScore, 0.95) AS firstPartyFraudThreshold

MATCH(c:Client)
WHERE c.firstPartyFraudScore > firstPartyFraudThreshold
SET c:FirstPartyFraudster;

以上,到目前為止的步驟是,我們先識別出有共享個資的客戶們,並用 WCC 演算法將其分群,再用 Node similarity 找出共享很多個資因而高度相似的客戶,計算其相似度分數,最後再用 Weighted Degree Centrality,把這些分數加總,作為第一方詐欺總評分。
然後取評分最高的一定比例,直接標籤為第一方詐欺犯。

接下來我們要以第一方詐欺犯為基礎延伸出去,找出可能的第二方詐欺犯。

從第一方詐欺找到金錢驢子

在接下來練習中,我想在 PaySim 資料集中檢測錢騾,也就是被犯罪分子招募來幫助洗錢。一個基本假設是,和第一方欺詐犯有資金往來的客戶,會是第二方欺詐的嫌疑人,所以要識別和探索第一方欺詐者與其他客戶間的交易,找出可能支持第一方欺詐者但未被確定為潛在第一方欺詐者的客戶。
先簡單看一下有哪些用戶和第一方詐欺犯有交易,從圖形可以初步看出,某些客戶和其他多位客戶有密集的交易,可能需要關注。

MATCH p = (:FirstPartyFraudster)-[]-(:Transaction)-[]-(c:Client)
WHERE NOT c:FirstPartyFraudster
RETURN p;

https://ithelp.ithome.com.tw/upload/images/20220911/20092912DhqIbo9zdZ.png

查看這些交易都是哪些類型,從查詢結果得知,全部都是用戶間的直接轉帳,果然符合錢驢的行為 XD

MATCH (:FirstPartyFraudster)-[]-(txn:Transaction)-[]-(c:Client)
WHERE NOT c:FirstPartyFraudster
UNWIND labels(txn) AS transactionType
RETURN transactionType, count(*) AS freq;

與第一方詐欺犯有交易的客戶,都先標籤為第二方詐欺嫌疑人,並建立新的關聯,紀錄轉出和轉入的交易總額,方便後續先針對大量交易做調查。

MATCH (c1:FirstPartyFraudster)-[]->(t:Transaction)-[]->(c2:Client)
WHERE NOT c2:FirstPartyFraudster
WITH c1, c2, sum(t.amount) AS totalAmount
SET c2:SecondPartyFraudSuspect
CREATE (c1)-[:TRANSFER_TO {amount:totalAmount}]->(c2);

分析第二方詐欺的族群 - WCC 演算法

為了將第二方詐欺分群,再次使用 WCC 演算法,並將焦點放在比較大的第二方詐欺群體。
這次改用轉帳交易的關係來分群,用 TRANSFER_TO 關聯建立新的 Projection graph,並將交易總額也寫入記憶體,方便後續過濾大宗交易。

CALL gds.graph.project('SecondPartyFraudNetwork',
    'Client',
    'TRANSFER_TO',
    {relationshipProperties:'amount'}
);

使用 WCC 演算法分群後,設定這些客戶們的第二方詐欺群組 id

CALL  gds.wcc.stream('SecondPartyFraudNetwork')
YIELD nodeId, componentId as groupId
WITH  gds.util.asNode(nodeId) AS client, groupId
WITH  groupId, collect(client) AS group
WITH  groupId, size(group) AS groupSize, group
WHERE groupSize > 1
UNWIND group AS client
    MATCH (c:Client {id:client.id})
    SET c.secondPartyFraudGroupId = groupId;

重新用圖形化表示,找出比較大的第二方詐騙集團

CALL  gds.wcc.stream('SecondPartyFraudNetwork')
YIELD nodeId, componentId as groupId
WITH  groupId, collect(nodeId) AS group
WITH  groupId, size(group) AS groupSize
WHERE groupSize > 5
MATCH p = (:FirstPartyFraudster)-[:TRANSFER_TO]-(c:Client)
WHERE NOT c:FirstPartyFraudster AND c.secondPartyFraudGroupId = groupId
RETURN p;

以轉帳總額找出影響力大的錢驢客戶 - PageRank 演算法

使用 PageRank 演算法,根據轉帳總額排名並給予分數,分數高者直接加上第二方詐欺的標籤。

CALL gds.pageRank.stream('SecondPartyFraudNetwork', {
    relationshipWeightProperty:'amount'
})  YIELD nodeId, score
WITH gds.util.asNode(nodeId) AS client, score AS pageRankScore

WHERE client.secondPartyFraudGroupId IS NOT NULL AND
      pageRankScore > 0.6 AND
      NOT client:FirstPartyFraudster

MATCH(c:Client {id:client.id})
    SET c:SecondPartyFraud
    SET c.secondPartyFraudScore = pageRankScore;

以上,我們用演算法,從多個角度逐步把最有可能的第一方詐欺群體找出後,再把與第一方詐欺犯有交易的客戶,且重要性高(總金額達到一定比例),直接標籤為錢驢。

國內外應用案例

國外知名大廠如 eBay, Adobe, IBM, volvo, NASA, Cisco, HP, Airbnb, Microsoft,都是 Neo4j 的客戶,其中跟金融詐欺檢測有關的案例是 ICIJ巴拿馬文件 ;至於國內,就我所知已有台灣金控龍頭採用 Neo4j,是由日新科技協助導入。

國內越來越多金融業開始評估 Neo4j,除了最受歡迎的詐欺檢測之外,對於營銷、風控、智能客服優化以及產品預測也是非常適合的應用。

參考資源

https://www.sisu.io/posts/paysim
https://www.sisu.io/posts/paysim-part2
https://www.bnc-technology.com/neo4j.html
https://www.experian.co.uk/blogs/latest-thinking/fraud-prevention/what-is-first-second-and-third-party-fraud/
https://neo4j.com/use-cases/fraud-detection/
https://www.tpisoftware.com/tpu/articleDetails/2651


圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言